#include "stdafx.h"
#include "actor_manager.hpp"
#include <stdexcept>
#include <boost/tuple/tuple.hpp>
#include <algorithm>


using namespace FndGame2D ;

void FndGame2D::thinker_manager::add_thinker(std::string& name, thinker* thinker_ptr) {
  if(is_add_thinker_safe())
    safe_add_thinker(name, thinker_ptr) ;
  else {
    m_safe_add.push_back(make_pair(name, thinker_ptr)) ;
  }
}

bool FndGame2D::thinker_manager::is_add_thinker_safe()const {
  return !m_update_guard ;
}

bool FndGame2D::thinker_manager::is_delte_thinker_safe()const {
  return !m_update_guard ;
}

void FndGame2D::thinker_manager::safe_add_thinker(std::string& name, thinker* thinker_ptr) {

  if(m_thinker_map.find(name) != m_thinker_map.end())
    throw std::range_error("thinker has exist!") ;
  m_thinker_map.insert(name, thinker_ptr) ;
  if(!thinker_ptr->has_property(HAS_MANAGER))
    thinker_ptr->add_property<thinker_manager*>(HAS_MANAGER, this) ;
  else thinker_ptr->reset_property<thinker_manager*>(HAS_MANAGER, this) ;
  if(thinker_ptr->get_property<bool>(drawer::can_render, false)) {
    m_drawers.push_back(static_cast<drawer*>(thinker_ptr)) ;
    m_is_need_sort = true ;
  }
}

void FndGame2D::thinker_manager::update_thinkers(float time_inteval) {
    
        
    m_update_guard = true ;
    m_total_update_time += time_inteval ;
   
    if(!m_safe_add.empty()) {
        std::string name ;
        thinker* thinker_ptr ;
        for(add_thinker_vector_type::iterator it = m_safe_add.begin() ;
            it != m_safe_add.end() ;
            ++it) {
            boost::tie(name, thinker_ptr) = *it ;
            safe_add_thinker(name, thinker_ptr) ;
        }
        m_safe_add.clear() ;
    }
    
    if(!m_safe_delete.empty()) {
        for(remove_thinker_set_type::iterator it = m_safe_delete.begin() ;
            it != m_safe_delete.end() ;
            ++it) {
	  safe_remove_thinker(*it) ;
        }
        m_safe_delete.clear() ;
    }


    while(!m_message_queue.empty() 
          && (m_message_queue.top().action_time <= m_total_update_time)) {

        handle_action_not_care_time(m_message_queue.top()) ;
        m_message_queue.pop() ;
    }
  

    for(thinker_ptr_map_type::iterator it = m_thinker_map.begin() ;
        it != m_thinker_map.end() ;
        ++it) {
        (*it).second->update(time_inteval) ;
    }
    m_update_guard = false ;

}

void FndGame2D::thinker_manager::safe_remove_thinker(const std::string& name, bool must_be_existed) {
  if(m_thinker_map.find(name) == m_thinker_map.end()) {
    if(must_be_existed)
      throw std::range_error("thinker does not exist!") ;
    return ;
  }
  
  thinker* p = m_thinker_map.find(name)->second ; 
  if(p->get_property(drawer::can_render, false))
    m_drawers.erase(std::remove_if(m_drawers.begin(), m_drawers.end(), 
				   drawer_level_find(static_cast<drawer*>(p)))) ;
  m_thinker_map.erase(name) ;

}

void FndGame2D::thinker_manager::remove_thinker(const std::string& name, bool must_be_existed) {
  if(is_delte_thinker_safe())
    safe_remove_thinker(name, must_be_existed) ;
  else
    m_safe_delete.insert(name) ;
}


void FndGame2D::thinker_manager::render_drawers(sf::RenderTarget& app) {
  app.SetView(*m_view) ;
  if(m_is_need_sort) {
    m_is_need_sort = false ;
    std::sort(m_drawers.begin(), m_drawers.end(), drawer_level_compare()) ;
  }
  for(drawer_map_type::iterator it = m_drawers.begin() ;
      it != m_drawers.end() ;
      ++it) {
    if((*it)->get_property<bool>(drawer::is_show, false))
      (*it)->render(app) ;
  }
  app.SetView(app.GetDefaultView()) ;
}

thinker* FndGame2D::thinker_manager::get_thinker(const std::string& name) {
  if(m_thinker_map.find(name) == m_thinker_map.end())
    return 0 ;
  return &m_thinker_map[name] ;
}
bool FndGame2D::thinker_manager::handle_action_not_care_time(const thinker_manager::thinker_message_type& msg) {

  if(name() == msg.receiver) return message_handler.handle_message(msg.msg,msg) ;
  if(get_thinker(msg.receiver)) return get_thinker(msg.receiver)->handle_message(msg) ;
  return false ;
}
bool FndGame2D::thinker_manager::do_handle_message(
					      const thinker_manager::thinker_message_type& msg) {
  if(msg.action_time <= 0.0f ) {
    handle_action_not_care_time(msg) ;
  } else {
   
    normal_thinker_message_type queue_message = msg ;
    queue_message.action_time += m_total_update_time ;
    m_message_queue.push(queue_message) ;
  
  }
  return true ;
}
/*
void FndGame2D::thinker_manager::handle_message(
					      const thinker_manager::thinker_message_type& msg) {
  if(msg.action_time <= 0.0f && handle_default_system_message(msg)) {
  } else {
    system_message_type sys_msg = msg ;
    sys_msg.action_time += m_total_update_time ;
    m_sys_message_queue.push(sys_msg) ;
  }
}
*/

bool FndGame2D::thinker_manager::add_a_thinker(const thinker_message_type& msg) {
    thinker* who = boost::any_cast<thinker*>(msg.param) ;
    std::string name = msg.sender ;
    add_thinker(name, who) ;
    return true ;
}

bool FndGame2D::thinker_manager::remove_a_thinker(const thinker_message_type& msg) {
  remove_thinker(msg.sender) ;
  return true ;
}

sf::Vector2f FndGame2D::thinker_manager::get_mouse_pos_in_render_view(const std::string&) const{
  float mouse_x = static_cast<float>(m_window.GetInput().GetMouseX()) ;
  float mouse_y = static_cast<float>(m_window.GetInput().GetMouseY()) ;
  if(m_view) {
    const sf::Vector2f& view_center = m_view->GetCenter() ;
    const sf::Vector2f& view_half_size = m_view->GetHalfSize() ;
    mouse_x += view_center.x - view_half_size.x ;
    mouse_y += view_center.y - view_half_size.y ;
  }
  return sf::Vector2f(mouse_x, mouse_y) ;
}

FndGame2D::thinker_manager::thinker_manager(sf::RenderWindow& r, const std::string& name) :
  drawer(name),
  m_update_guard(false),
  m_safe_add(),
  m_safe_delete(),
  m_total_update_time(0.0f),
  m_view(NULL),
  m_window(r),
  m_is_need_sort(false)
{
    m_view = new sf::View(sf::FloatRect(0,0, 640,480)) ;
    updater2.connect(sigc::mem_fun(this, &thinker_manager::update_thinkers)) ;
    renderer = boost::bind(&thinker_manager::render_drawers, this, _1) ;
    message_handler.add_handle(ADD_THINKER, boost::bind(&thinker_manager::add_a_thinker, this, _1)) ;
    message_handler.add_handle(REMOVE_THINKER, boost::bind(&thinker_manager::remove_a_thinker, this, _1)) ;
}

FndGame2D::thinker_manager::~thinker_manager() {
    if(m_view)
        delete m_view ;
    m_view = 0 ;
    m_drawers.clear() ;
    m_thinker_map.clear() ;
}
const std::string FndGame2D::thinker_manager::ADD_THINKER = std::string("add_thinker") ;
const std::string FndGame2D::thinker_manager::REMOVE_THINKER = std::string("remove_actor") ;
const std::string FndGame2D::thinker_manager::HAS_MANAGER("has_manager") ;
